package cn.fintecher.pangolin.service.dataimp.task;

import cn.fintecher.pangolin.common.enums.BaseSelect;
import cn.fintecher.pangolin.common.enums.CaseDataStatus;
import cn.fintecher.pangolin.common.enums.PublicCaseFlag;
import cn.fintecher.pangolin.common.enums.StrategyDistributeType;
import cn.fintecher.pangolin.common.exception.BadRequestException;
import cn.fintecher.pangolin.common.model.UserModel;
import cn.fintecher.pangolin.common.utils.RepositoryUtil;
import cn.fintecher.pangolin.common.utils.ZWDateUtil;
import cn.fintecher.pangolin.entity.elastic.CaseTransferLog;
import cn.fintecher.pangolin.entity.elastic.CollectorModel;
import cn.fintecher.pangolin.entity.elastic.Strategy;
import cn.fintecher.pangolin.entity.mysql.CaseInfo;
import cn.fintecher.pangolin.entity.mysql.Department;
import cn.fintecher.pangolin.entity.mysql.User;
import cn.fintecher.pangolin.service.dataimp.elastic.CaseTransferLogElastic;
import cn.fintecher.pangolin.service.dataimp.model.StrategyExecuteModel;
import cn.fintecher.pangolin.service.dataimp.repository.CaseInfoRepository;
import cn.fintecher.pangolin.service.dataimp.repository.DepartmentRepository;
import cn.fintecher.pangolin.service.dataimp.repository.UserRepository;
import cn.fintecher.pangolin.service.dataimp.service.BaseService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;

/**
 * @Auther: xiaqun
 * @Description:
 * @Date: 10:22 2019/9/24
 */

@Service("caseInfoStrategyTask")
public class CaseInfoStrategyTask {
    private static final Logger log = LoggerFactory.getLogger(CaseInfoStrategyTask.class);

    @Autowired
    private CaseInfoRepository caseInfoRepository;

    @Autowired
    private CaseTransferLogElastic caseTransferLogElastic;

    @Autowired
    private DepartmentRepository departmentRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private BaseService baseService;

    /**
     * 策略分案
     */
    @Async
    @Transactional
    public CompletableFuture<StrategyExecuteModel> strategyDistribute(Strategy strategy, List<CaseInfo> caseInfoList, UserModel userModel) {
        log.info("策略{}执行开始,共需分配{}个案件", strategy.getName(), strategy.getModels().isEmpty() ? 0 : caseInfoList.size());
        StrategyExecuteModel model = new StrategyExecuteModel();
        model.setStrategy(strategy);
        model.setNumber(caseInfoList.size());
        List<CollectorModel> models = strategy.getModels();
        if (models.isEmpty()) {
            log.info("策略{}分案模型为空.............", strategy.getName());
            return CompletableFuture.completedFuture(model);
        }
        caseInfoList.sort(Comparator.comparing(CaseInfo::getOverdueAmtTotal)); //委案金额正序排列
        Map<String, List<CaseInfo>> map;
        if (strategy.getType().equals(StrategyDistributeType.DEPARTMENT)) { //按部门分
            if (strategy.getIsComplex().equals(BaseSelect.NO)) { //设置上限分配
                map = manualDistribute(caseInfoList, models);
            } else { //综合分配
                map = complexDistribute(caseInfoList, models);
            }
            distribute(map, userModel, 0);
            //还有剩余则综合分配
            if (!caseInfoList.isEmpty()) {
                map = complexDistribute(caseInfoList, models);
                distribute(map, userModel, 0);
            }
        } else { //按用户分
            if (strategy.getIsComplex().equals(BaseSelect.NO)) { //设置上限分配
                map = manualDistribute(caseInfoList, models);
            } else { //综合分配
                map = complexDistribute(caseInfoList, models);
            }
            distribute(map, userModel, 1);
            //还有剩余则综合分配
            if (!caseInfoList.isEmpty()) {
                map = complexDistribute(caseInfoList, models);
                distribute(map, userModel, 1);
            }
        }
        log.info("策略{}执行完成", strategy.getName());
        return CompletableFuture.completedFuture(model);
    }

    /**
     * 设置上限分配
     */
    private Map<String, List<CaseInfo>> manualDistribute(List<CaseInfo> caseInfos, List<CollectorModel> models) {
        Map<String, List<CaseInfo>> map = new HashMap<>();
        if (models.size() == 1) { //只有一个 直接分配
            CollectorModel collectorModel = models.get(0);
            List<CaseInfo> baseCaseList = cycleDistribute(collectorModel.getNumber(), caseInfos);
            List<CaseInfo> list = new ArrayList<>(baseCaseList);
            map.put(collectorModel.getId(), list);
        } else { //分配多个
            for (CollectorModel model : models) {
                List<CaseInfo> baseCaseList = cycleDistribute(model.getNumber(), caseInfos);
                List<CaseInfo> list = new ArrayList<>(baseCaseList);
                map.put(model.getId(), list);
            }
        }
        return map;
    }

    /**
     * 综合分配
     */
    private Map<String, List<CaseInfo>> complexDistribute(List<CaseInfo> caseInfos, List<CollectorModel> models) {
        Map<String, List<CaseInfo>> map = new HashMap<>();
        if (models.size() == 1) { //只有一个 全部分配
            CollectorModel collectorModel = models.get(0);
            List<CaseInfo> list = new ArrayList<>(caseInfos);
            map.put(collectorModel.getId(), list);
        } else { //分配多个
            processComplex(caseInfos, models, map);
        }
        return map;
    }

    /**
     * 上限循环分配
     */
    private List<CaseInfo> cycleDistribute(int number, List<CaseInfo> caseInfos) {
        List<CaseInfo> list = new ArrayList<>();
        for (int i = 0; i < number; i++) {
            if (caseInfos.isEmpty()) {
                break;
            }
            if (i % 2 == 0) { //偶数时
                CaseInfo caseInfo = caseInfos.get(0);
                list.add(caseInfo);
                caseInfos.remove(caseInfo);
            } else { //奇数时
                CaseInfo caseInfo = caseInfos.get(caseInfos.size() - 1);
                list.add(caseInfo);
                caseInfos.remove(caseInfo);
            }
        }
        return list;
    }

    /**
     * 综合多分案
     */
    private void processComplex(List<CaseInfo> caseInfos, List<CollectorModel> models, Map<String, List<CaseInfo>> map) {
        int avgNumber = (int) Math.ceil((double) caseInfos.size() / (double) models.size()); //平均案件数
        double amt = 0d;
        for (CaseInfo caseInfo : caseInfos) {
            amt = amt + caseInfo.getOverdueAmtTotal();
        }
        double avgAmt = amt / (double) caseInfos.size(); //平均案件金额
        for (CollectorModel model : models) {
            List<CaseInfo> list = complexCycleDistribute(caseInfos, avgNumber, avgAmt);
            if (map.containsKey(model.getId())) {
                List<CaseInfo> caseList = map.get(model.getId());
                caseList.addAll(list);
                map.put(model.getId(), caseList);
            } else {
                map.put(model.getId(), list);
            }
        }
        if (!caseInfos.isEmpty()) {
            processComplex(caseInfos, models, map);
        }
    }

    /**
     * 综合循环分配
     */
    private List<CaseInfo> complexCycleDistribute(List<CaseInfo> caseInfos, int avgNumber, double avgAmt) {
        List<CaseInfo> list = new ArrayList<>();
        int nowNumber = 0; //当前案件数
        double nowAmt = 0d; //当前案件金额
        if (nowNumber >= avgNumber) { //当前案件数大于等于平局案件数
            return list;
        } else { //当前案件数小于平均案件数
            if (nowAmt >= avgAmt) { //当前案件金额大于等于平均案件金额
                return list;
            }
        }
        for (int i = 0; i < avgNumber; i++) {
            if (caseInfos.isEmpty()) {
                break;
            }
            if (i % 2 == 0) { //偶数时
                CaseInfo caseInfo = caseInfos.get(0);
                list.add(caseInfo);
                caseInfos.remove(caseInfo);
                nowNumber++;
                nowAmt += caseInfo.getOverdueAmtTotal();
            } else { //奇数时
                CaseInfo baseCase = caseInfos.get(caseInfos.size() - 1);
                list.add(baseCase);
                caseInfos.remove(baseCase);
                nowNumber++;
                nowAmt += baseCase.getOverdueAmtTotal();
            }
        }
        return list;
    }

    /**
     * 分配
     */
    private void distribute(Map<String, List<CaseInfo>> map, UserModel userModel, int flag) {
        List<CaseInfo> caseInfos = new ArrayList<>();
        List<CaseTransferLog> caseTransferLogList = new ArrayList<>();
        for (Map.Entry<String, List<CaseInfo>> entry : map.entrySet()) {
            List<CaseInfo> list = entry.getValue();
            list.forEach(e -> e.setStrategyId(null));
            if (flag == 0) { //按部门分
                Department department = departmentRepository.findById(entry.getKey()).orElseThrow(() -> new BadRequestException(null, "department", "department.is.null"));
                for (CaseInfo caseInfo : list) {
                    caseInfo.setDepartment(department);
                    String outUserId = Objects.isNull(caseInfo.getCurrentCollector()) ? null : caseInfo.getCurrentCollector().getId();
                    caseInfo.setCurrentCollector(null);
                    caseInfo.setCaseDataStatus(CaseDataStatus.IN_POOL);
                    caseInfo.setPublicCaseFlag(PublicCaseFlag.NO_PUBLIC_CASE);
                    caseInfo.setTransferDate(ZWDateUtil.getNowDateTime());
                    caseInfo.setOperator(userModel.getRealName());
                    caseInfo.setOperatorTime(ZWDateUtil.getNowDateTime());
                    caseInfo.setStrategyId(null);
                    caseInfo.setLastContactResult(null);
                    caseInfo.setLastContactDate(null);
                    caseTransferLogList.add(baseService.createCaseTranferLog(caseInfo.getId(), "策略分案:分案到【".concat(department.getName()).concat("】"), userModel, outUserId, null, caseInfo.getLeftAmt()));
                    caseInfos.add(caseInfo);
                }
            } else { //按催收员分
                User user = userRepository.findById(entry.getKey()).orElseThrow(() -> new BadRequestException(null, "department", "user.is.null"));
                for (CaseInfo caseInfo : list) {
                    String outUserId = Objects.isNull(caseInfo.getCurrentCollector()) ? null : caseInfo.getCurrentCollector().getId();
                    caseInfo.setCurrentCollector(user);
                    caseInfo.setDepartment(null);
                    caseInfo.setCaseDataStatus(CaseDataStatus.IN_POOL);
                    caseInfo.setPublicCaseFlag(PublicCaseFlag.NO_PUBLIC_CASE);
                    caseInfo.setTransferDate(ZWDateUtil.getNowDateTime());
                    caseInfo.setOperator(userModel.getRealName());
                    caseInfo.setOperatorTime(ZWDateUtil.getNowDateTime());
                    caseInfo.setStrategyId(null);
                    caseInfo.setLastContactResult(null);
                    caseInfo.setLastContactDate(null);
                    caseTransferLogList.add(baseService.createCaseTranferLog(caseInfo.getId(), "策略分案:分案到".concat(user.getRealName()).concat("】"), userModel, outUserId, user.getId(), caseInfo.getLeftAmt()));
                    caseInfos.add(caseInfo);
                }
            }
        }
        //保存分配案件
        if (!caseInfos.isEmpty()) {
            RepositoryUtil.saveData(caseInfoRepository, caseInfos);
        }
        //保存流转记录
        if (!caseTransferLogList.isEmpty()) {
            RepositoryUtil.saveData(caseTransferLogElastic, caseTransferLogList);
        }
    }
}
